﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.IO;

namespace JVMTI_CONN_CONNECT
{
    //let's just follow Linux objdump notation -M Intel --line-number
    public class ValueComparator : IComparer<KeyValuePair<Int32, Int32>>
    {
        public int Compare(KeyValuePair<int, int> x, KeyValuePair<int, int> y)
        {
            if (x.Value > y.Value)
                return -1;
            if (x.Value > y.Value)
                return 1;
            return 0;
        }
    }

    public class MethodInfo
    {
        public String Name { get; set; }
        public List<KeyValuePair<Int32, Int32>> LineTable { get; set; }
        public String ClassID { get; set; }
    }

    public class StackTraceItem
    {
        public string FileName { get; set; }
        public int LineNumber { get; set; }
        public int Depth { get; set; }
        protected string MethodId { get; set; }
        protected string Location { get; set; }
        protected string MethodName { get; set; }

        private static Dictionary<String, MethodInfo> methodStrings;
        private static Dictionary<String, String> methodClassStrings;
        private static String currentFileName;

        private static void LoadMethodStrings(String fileName)
        {
            currentFileName = fileName;
            String[] classTable = File.ReadAllLines(fileName + ".classtable");
            methodClassStrings = new Dictionary<string, string>();
            foreach (string s in classTable)
            {
                String[] pieces = s.Split(',');
                if (pieces.Length < 2) continue;
                String classID = pieces[1];
                String classFile = pieces[3];
                methodClassStrings.Add(classID, classFile);
            }

            String[] methodTable = File.ReadAllLines(fileName + ".methods");
            methodStrings = new Dictionary<string, MethodInfo>();
            foreach (string s in methodTable)
            {
                String[] pieces = s.Split(',');
                if (pieces.Length < 2) continue;
                MethodInfo minfo = new MethodInfo();
                String methodID = pieces[1];
                minfo.Name = pieces[2];
                minfo.ClassID = pieces[4];
                String lines = pieces[5];
                String[] linetable = lines.Split(';');
                int n = Int32.Parse(linetable[0]);
                if (n <= 0)
                    minfo.LineTable = null;
                else
                {
                    minfo.LineTable = new List<KeyValuePair<int, int>>();
                    for (int i = 1; i < n; i += 2)
                    {
                        minfo.LineTable.Add(new KeyValuePair<int, int>(
                            Int32.Parse(linetable[i], System.Globalization.NumberStyles.HexNumber),
                            Int32.Parse(linetable[i + 1], System.Globalization.NumberStyles.HexNumber)));
                    }
                }
                methodStrings.Add(methodID, minfo);
            }
        }

        public StackTraceItem(string methodId, string location, int depth, string fileNameInUse)
        {
            MethodId = methodId;
            Location = location;
            Depth = depth;
            FileName = null;
            LineNumber = -1;

            //                       methodsID,        location,     depth = depth,    PingPong
            if (methodStrings == null || fileNameInUse != currentFileName)
                LoadMethodStrings(fileNameInUse);

            if (methodStrings.ContainsKey(methodId))
            {
                var minfo = methodStrings[methodId];
                var lineTable = minfo.LineTable;
                if (lineTable != null)
                {
                    int lineNumber = lineTable.BinarySearch(new KeyValuePair<int, int>(0, Int32.Parse(location, System.Globalization.NumberStyles.HexNumber)),
                        new ValueComparator());
                    if (lineNumber < 0)
                        lineNumber = (~lineNumber) - 1;
                    LineNumber = lineTable[lineNumber].Key;
                }

                MethodName = minfo.Name;

                if (methodClassStrings.ContainsKey(minfo.ClassID))
                    FileName = methodClassStrings[minfo.ClassID];
            }
        }
        public override string ToString()
        {
            return string.Format("[{0}]{1} : {2}", MethodName, FileName, LineNumber);
        }
    }

    public class StackTrace : List<StackTraceItem>
    {
        internal static StackTrace EmptyTrace;
        static StackTrace()
        {
            EmptyTrace = null;
        }
        static Regex parser = new Regex(@"(\d+);([A-Fa-fxX0-9;]*)");
        static Regex extractor = new Regex(@"([A-Fa-fxX0-9]+);([A-Fa-fxX0-9]+);*");
        public StackTrace(string stringRep, string fileName)
        {
            var pairs = parser.Match(stringRep).Groups[2];
            var components = extractor.Match(pairs.Value);
            if (!components.Success) throw new FormatException(string.Format("{0} is not an acceptable stack trace", stringRep));
            for (int i = 1; i < components.Groups.Count; i += 2)
            {
                this.Add(new StackTraceItem(components.Groups[i].Value, components.Groups[i + 1].Value, i, fileName));
            }
        }
    }
}
